﻿// --------------------------------------------------------------------------------------------------------------------
// <copyright file="Subscriber.cs" company="JDML Consulting Ltd.">
//   Copyright 2011 Photon Project - http://photon.codeplex.com
// </copyright>
// <summary>
//   Defines the Subscriber type.
// </summary>
// --------------------------------------------------------------------------------------------------------------------

namespace Photon.Assets
{
    using System.Collections.Generic;
    using System.Collections.ObjectModel;
    using Microsoft.VisualStudio.TestTools.UnitTesting;

    public class Subscriber
    {
        #region Fields

        private readonly IList<LogEntry> _internalLogEntries;
        private ReadOnlyCollection<LogEntry> _logEntries;

        #endregion

        public Subscriber()
        {
            _internalLogEntries = new List<LogEntry>();
        }

        public ReadOnlyCollection<LogEntry> LogEntries
        {
            get { return _logEntries ?? (_logEntries = new ReadOnlyCollection<LogEntry>(_internalLogEntries)); }
        }

        protected object Result { get; private set; }

        public TReturn OnInvokeFunc<TReturn>()
        {
            AddLogEntry();
            return (TReturn)Result;
        }

        public TReturn OnInvokeFunc<TArg0, TReturn>(TArg0 arg0)
        {
            AddLogEntry(arg0);
            return (TReturn)Result;
        }

        public TReturn OnInvokeFunc<TArg0, TArg1, TReturn>(TArg0 arg0, TArg1 arg1)
        {
            AddLogEntry(arg0, arg1);
            return (TReturn)Result;
        }

        public TReturn OnInvokeFunc<TArg0, TArg1, TArg2, TReturn>(TArg0 arg0, TArg1 arg1, TArg2 arg2)
        {
            AddLogEntry(arg0, arg1, arg2);
            return (TReturn)Result;
        }

        public TReturn OnInvokeFunc<TArg0, TArg1, TArg2, TArg3, TReturn>(TArg0 arg0, TArg1 arg1, TArg2 arg2, TArg3 arg3)
        {
            AddLogEntry(arg0, arg1, arg2, arg3);
            return (TReturn)Result;
        }

        public void OnInvokeAction()
        {
            AddLogEntry();
        }

        public void OnInvokeAction<TArg1>(TArg1 arg1)
        {
            AddLogEntry(arg1);
        }

        public void OnInvokeAction<TArg1, TArg2>(TArg1 arg1, TArg2 arg2)
        {
            AddLogEntry(arg1, arg2);
        }

        public void OnInvokeAction<TArg1, TArg2, TArg3>(TArg1 arg1, TArg2 arg2, TArg3 arg3)
        {
            AddLogEntry(arg1, arg2, arg3);
        }

        public void OnInvokeAction<TArg1, TArg2, TArg3, TArg4>(TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4)
        {
            AddLogEntry(arg1, arg2, arg3, arg4);
        }

        // ReSharper disable UnusedMember.Local
        
        private void OnInvokePrivateAction()
        {
            AddLogEntry();
        }

        private void OnInvokePrivateAction<TArg1>(TArg1 arg1)
        {
            AddLogEntry(arg1);
        }

        private void OnInvokePrivateAction<TArg1, TArg2>(TArg1 arg1, TArg2 arg2)
        {
            AddLogEntry(arg1, arg2);
        }

        private void OnInvokePrivateAction<TArg1, TArg2, TArg3>(TArg1 arg1, TArg2 arg2, TArg3 arg3)
        {
            AddLogEntry(arg1, arg2, arg3);
        }

        private void OnInvokePrivateAction<TArg1, TArg2, TArg3, TArg4>(TArg1 arg1, TArg2 arg2, TArg3 arg3, TArg4 arg4)
        {
            AddLogEntry(arg1, arg2, arg3, arg4);
        }

        private TReturn OnInvokePrivateFunc<TReturn>()
        {
            AddLogEntry();
            return (TReturn)Result;
        }

        private TReturn OnInvokePrivateFunc<TArg0, TReturn>(TArg0 arg0)
        {
            AddLogEntry(arg0);
            return (TReturn)Result;
        }

        private TReturn OnInvokePrivateFunc<TArg0, TArg1, TReturn>(TArg0 arg0, TArg1 arg1)
        {
            AddLogEntry(arg0, arg1);
            return (TReturn)Result;
        }

        private TReturn OnInvokePrivateFunc<TArg0, TArg1, TArg2, TReturn>(TArg0 arg0, TArg1 arg1, TArg2 arg2)
        {
            AddLogEntry(arg0, arg1, arg2);
            return (TReturn)Result;
        }

        private TReturn OnInvokePrivateFunc<TArg0, TArg1, TArg2, TArg3, TReturn>(TArg0 arg0, TArg1 arg1, TArg2 arg2, TArg3 arg3)
        {
            AddLogEntry(arg0, arg1, arg2, arg3);
            return (TReturn)Result;
        }


        // ReSharper restore UnusedMember.Local

        private void AddLogEntry(params object[] arguments)
        {
            _internalLogEntries.Add(new LogEntry(arguments));
        }

        public void ClearLog()
        {
            _internalLogEntries.Clear();
        }

        public void AssertLogEntryCount(int expected)
        {
            Assert.AreEqual(expected, LogEntries.Count, "The subscriber log does not contain the expected number of entries.");
        }

        public void AssertLogEntries(IList<object[]> allArguments)
        {
            Assert.AreEqual(allArguments.Count, LogEntries.Count);
            for (var i = 0; i < allArguments.Count; i++)
            {
                CollectionAssert.AreEqual(allArguments[i], LogEntries[i].Arguments);
            }
        }

        public void AssertSingleLogEntry(params object[] arguments)
        {
            Assert.AreEqual(1, LogEntries.Count, "More than the expected log entries, expected {0}, actual {1}.", 1, LogEntries.Count);
            CollectionAssert.AreEqual(arguments, LogEntries[0].Arguments, "The log entry arguments did not match.");
        }

        public void SetReturn(object result)
        {
            Result = result;
        }

        #region Nested type: LogEntry

        public class LogEntry
        {
            public LogEntry(params object[] arguments)
            {
                Arguments = new ReadOnlyCollection<object>(arguments);
            }

            public ReadOnlyCollection<object> Arguments { get; private set; }
        }

        #endregion
    }
}